﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using CoreEx.Common.Ioc;
using CoreEx.Services;
using LinFu.IoC.Interfaces;

namespace CoreEx.Common.Implementation.Ioc
{
    /// <summary>
    /// Determines the assemblies that should be weaved before they get loaded 
    /// into an <see cref="IServiceContainer"/> instance.
    /// </summary>
    public class WeaveTargetExtractor : MarshalByRefObject
    {                        
        /// <summary>
        /// Inspects the <paramref name="assemblyFiles"/> and searches for implementations 
        /// of the <see cref="IAssemblyWeaver"/> interface.
        /// </summary>
        /// <remarks>
        /// The reason that this class inherits from <see cref="MarshalByRefObject"/>, is that it is 
        /// used in a secondary <see cref="AppDomain"/>.
        /// </remarks>
        /// <seealso cref="IWeaveTargetLoader"/>
        /// <param name="assemblyFiles">A list of files currently beeing loaded onto a <see cref="IServiceContainer"/> instance.</param>
        /// <returns>A list of weave targets along with the <see cref="IAssemblyWeaver"/> implemntations targeting each weave target.</returns>
        public IDictionary<string,IList<Type>> GetWeaveTargets(IEnumerable<string> assemblyFiles)
        {
            IDictionary<string,IList<Type>> assemblyWeavers 
                = new Dictionary<string, IList<Type>>();
                       
            foreach (var assemblyFile in assemblyFiles)
            {
                Assembly assembly = Assembly.LoadFrom(assemblyFile);
                var weaverTypes = assembly.GetTypes().Where(t => typeof (IAssemblyWeaver).IsAssignableFrom(t));
                foreach (var weaverType in weaverTypes)
                {
                    var attributes = weaverType.GetCustomAttributes(typeof (WeavesAttribute), true);
                    foreach (var attribute in attributes)
                    {
                        var targetAssembly = ((WeavesAttribute) attribute).AssemblyFile;
                        if (!assemblyWeavers.ContainsKey(targetAssembly))
                            assemblyWeavers.Add(targetAssembly,new List<Type>());
                        assemblyWeavers[targetAssembly].Add(weaverType);
                    }
                }
            }
            return assemblyWeavers;
        }        
    }
}